package gp;

import java.util.ArrayList;
import java.util.Stack;

public class Individuo {
	float fitness;
	Tree tree;
	
	
	public Individuo(float fitness, Tree tree){
		this.fitness = fitness;
		this.tree = tree;
	}
	
	public Individuo(float fitness, Stack<Symbol> symbolsStack){
		this.fitness = fitness;
		this.tree = new Tree(symbolsStack);
	}
	
	public Node get_root(){
		return this.tree.root;
	}
	
	public int get_height(){
		return this.tree.getHeight();
	}
	
	public ArrayList<Symbol> get_vector_height(int max_height){
		ArrayList<Symbol> vector = new ArrayList<Symbol>();
		tree.get_vector_height(max_height, vector);
		return vector;
	}
	
	private double calculate_order(ArrayList<Symbol> vector, int alphabet_size){
		double order = 0;
		for(int i = 0 ; i < vector.size(); i++){
			order += vector.get(i).getValor() * Math.pow(alphabet_size, vector.size() - i -1);
		}
		//Centraliza o ponto, garantindo que ele ocupará o centro do arco que lhe é cabido.
		return order;
	}
	
	public double calculate_angle_nonTerminals(Alphabet alphabet){
		int height = this.tree.getHeight();
		if (height == 0) {
			//Não há não-terminais na árvore
			return -1;
		}
		double angulo_total = 360;
		double divisor_primeiro_nivel = alphabet.non_terminals_size() - 1;
		double divisor = 1;
		double somaNiveis = 0;
		ArrayList<Symbol> vector_height_aux;
		
		//O primeiro nível tem um tratamento diferente porque não contém um símbolo vazio
		vector_height_aux = this.get_vector_height(0);
		//Haverá apenas não-terminais no primeiro nível
		somaNiveis += calculate_order(vector_height_aux, alphabet.non_terminals_size() - 1);
		
		for(int i = 1; i < height; i++){
			vector_height_aux = this.get_vector_height(i);
			divisor *= Math.pow(alphabet.non_terminals_size(), vector_height_aux.size());
			somaNiveis += calculate_order(vector_height_aux, alphabet.non_terminals_size()) / divisor;
		}
		
		somaNiveis +=  0.5f  / divisor;
		
		return (angulo_total / divisor_primeiro_nivel) * somaNiveis;
		
	}
	
	public double calculate_angle(Alphabet alphabet){
		int height = this.tree.getHeight();
		double angulo_total = 360;
		double divisor_primeiro_nivel;
		double divisor = 1;
		double somaNiveis = 0;
		ArrayList<Symbol> vector_height_aux;
		
		//O primeiro nível tem um tratamento diferente porque não contém um símbolo vazio
		vector_height_aux = this.get_vector_height(0);
		if (height == 0) {
			//Haverá apenas terminais no primeiro nível
			divisor_primeiro_nivel = alphabet.terminals_size() - 1;
			somaNiveis += calculate_order(vector_height_aux, alphabet.terminals_size() - 1);
		}
		else {
			//Haverá apenas não-terminais no primeiro nível
			divisor_primeiro_nivel = alphabet.non_terminals_size() - 1;
			somaNiveis += calculate_order(vector_height_aux, alphabet.non_terminals_size() - 1);
		}
		for(int i = 1; i < height; i++){
			vector_height_aux = this.get_vector_height(i);
			divisor *= Math.pow(alphabet.non_terminals_size(), vector_height_aux.size());
			somaNiveis += calculate_order(vector_height_aux, alphabet.non_terminals_size()) / divisor;
		}
		//O nível folha conterá os terminais, mas é preciso garantir que não se trata de uma árvore de altura 1, que já foi tratada antes do loop
		if (height > 0) {
			vector_height_aux = this.get_vector_height(height);
			divisor *= Math.pow(alphabet.terminals_size(), vector_height_aux.size());
			somaNiveis += calculate_order(vector_height_aux, alphabet.terminals_size()) / divisor;
		}
		
		somaNiveis += 0.5f / divisor;
		
		return (angulo_total / divisor_primeiro_nivel) * somaNiveis;
		
	}
}
